import pandas as pd
import numpy as np
# import matplotlib.pyplot as plt
import util_for_output_zh
from util_set_zh_matplot import plt
import seaborn as sns
from pathlib import Path
import pdb
import os

import util_for_output_zh

# 在查看数据前设置
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)

def main():
    # stage1()
    # stage2_NAN_process()
    stage3_outlier_process('zscore')

    # 使用示例
    # input_csv = 'filled_data_outlier_processed.csv'  # 替换为你的输入文件
    # output_csv = 'nipt_processed_results.csv'
    # processed_data = process_nipt_data(input_csv, output_csv)
    # print(processed_data.head())
def stage1():
    # 读取Excel文件
    df = pd.read_excel("附件.xlsx")

    # 保存为csv格式
    df.to_csv("附件.csv", index=False)

    # 输出前几行信息
    print(df.head())
'''
   序号  孕妇代码  年龄     身高    体重          \
        末次月经       IVF妊娠   检测日期  检测抽血次数   检测孕周      孕妇BMI    \
        原始读段数  在参考基因组上比对的比例   重复读段的比例  唯一比对的读段数     \
        GC含量  13号染色体的Z值  18号染色体的Z值  21号染色体的Z值   X染色体的Z值   Y染色体的Z值    Y染色体浓度    X染色体浓度  \
        13号染色体的GC含量  18号染色体的GC含量  21号染色体的GC含量  \
        被过滤掉读段数的比例 染色体的非整倍体 怀孕次数  生产次数 胎儿是否健康

'''

# NAN 代表 ​​“未发现非整倍体异常”​​，即染色体数目在检测范围内是正常的
def visualize_chromosome_category(df):
    df['染色体的非整倍体'] = df['染色体的非整倍体'].fillna("正常")
    """可视化染色体的非整倍体的分类情况"""
    # 显示分类值及其数量
    category_counts = df['染色体的非整倍体'].value_counts(dropna=False)
    print("\n染色体的非整倍体分类分布:")
    print(category_counts)
    
    # 可视化分类分布
    plt.figure(figsize=(12, 6))
    ax = sns.countplot(x='染色体的非整倍体', data=df, order=category_counts.index, hue=df['染色体的非整倍体'].isna())
    plt.title("染色体的非整倍体分类分布", fontsize=16)
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.ylim(0, 1000)
    
    # 在柱状图上添加数值标签
    for p in ax.patches:
        ax.annotate(f'{p.get_height()}', (p.get_x() + p.get_width() / 2., p.get_height()),
                    ha='center', va='center', xytext=(0, 10), textcoords='offset points')
    
    # plt.show()
    plt.savefig(os.path.join("visualization", f"染色体的非整倍体.png"), bbox_inches='tight')
    return df

from util_for_ques0 import parse_gestational_age,calculate_last_menstrual_date
def fill_missing_values(df):
    """填充缺失值：末次月经用检测日期和孕周反推，数值型用中位数，其他分类型用众数"""
    # 复制数据以避免修改原始数据
    df_filled = df.copy()

    # 1. 处理末次月经缺失值：用检测日期和检测孕周反推
    if '末次月经' in df_filled.columns and '检测日期' in df_filled.columns and '检测孕周' in df_filled.columns:
        # 解析孕周为天数
        df_filled['孕周天数'] = df_filled['检测孕周'].apply(parse_gestational_age)
        
        # 找出末次月经缺失的行
        missing_lmp = df_filled['末次月经'].isnull()
        print(f"\n需要填充的末次月经缺失值数量: {missing_lmp.sum()}")
        
        # 对缺失值进行填充
        if missing_lmp.sum() > 0:
            df_filled.loc[missing_lmp, '末次月经'] = df_filled.loc[missing_lmp].apply(
                lambda row: calculate_last_menstrual_date(row['检测日期'], row['孕周天数']), axis=1)
            
            # 检查是否还有未能填充的缺失值
            remaining_missing = df_filled['末次月经'].isnull().sum()
            if remaining_missing > 0:
                print(f"注意：仍有 {remaining_missing} 个末次月经缺失值无法通过反推填充，将使用众数填充")
                # 对仍缺失的值使用众数填充
                lmp_mode = df_filled['末次月经'].mode()[0]
                df_filled['末次月经'].fillna(lmp_mode, inplace=True)
        
        # 删除临时列
        if '孕周天数' in df_filled.columns:
            df_filled.drop('孕周天数', axis=1, inplace=True)
    
    return df_filled


def stage2_NAN_process():
    # 读取csv文件
    df = pd.read_csv("附件.csv")

    print("原始数据信息：")
    print(f"数据集形状: {df.shape}")
    

    # 染色体的非整倍体 情况 查看 & 填充 NAN 值
    df_chromosome_fill = visualize_chromosome_category(df)

    # fill 末次月经
    df_yuejing_fill = fill_missing_values(df_chromosome_fill)

    
    # 验证填充结果
    # print("\n填充后缺失值检查:")
    print(df_yuejing_fill.isnull().sum())
    
    
    # 可以在这里保存处理后的数据
    df_yuejing_fill.to_csv("filled_data.csv", index=False)
    print("\n处理后的数据已保存为 filled_data.csv")


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
from scipy import stats

def stage3_outlier_process(method='iqr', whisker=1.5, save_visualizations=True, output_file="filled_data_outlier_processed.csv"):
    """
    读取 filled_data.csv，进行异常值可视化和处理
    
    参数:
    - method: 异常值检测方法，可选 'iqr' (四分位法) 或 'zscore' (Z-score法)
    - whisker: IQR方法中的 whisker 系数，默认1.5
    - save_visualizations: 是否保存可视化结果
    - output_file: 处理后数据的保存路径
    """
    # 读取数据
    try:
        df = pd.read_csv("filled_data.csv")
        print(f"成功读取填充后的数据，共 {df.shape[0]} 行，{df.shape[1]} 列，准备进行异常值分析...")
    except FileNotFoundError:
        print("错误：未找到 filled_data.csv 文件，请检查路径是否正确")
        return
    except Exception as e:
        print(f"读取数据时发生错误: {str(e)}")
        return

    # 定义数值型特征列
    numeric_cols = [
        '年龄', '身高', '体重', '孕妇BMI', '原始读段数', '在参考基因组上比对的比例', '重复读段的比例',
        '唯一比对的读段数', 'GC含量', '13号染色体的Z值', '18号染色体的Z值', '21号染色体的Z值',
        'X染色体的Z值', 'Y染色体的Z值', 'Y染色体浓度', 'X染色体浓度',
        '13号染色体的GC含量', '18号染色体的GC含量', '21号染色体的GC含量', '被过滤掉读段数的比例'
    ]
    # 只保留存在于df中的列
    numeric_cols = [col for col in numeric_cols if col in df.columns]
    
    if not numeric_cols:
        print("未找到任何数值型列，无法进行异常值处理")
        return
        
    print(f"将对以下 {len(numeric_cols)} 个数值型列进行异常值处理: {', '.join(numeric_cols)}")

    # 创建可视化文件夹
    vis_dir = "visualization"
    if save_visualizations and not os.path.exists(vis_dir):
        os.makedirs(vis_dir)
        print(f"创建可视化文件夹: {vis_dir}")

    # 1. 可视化数值型特征的分布和异常值
    # 为每个特征创建单独的分布图和箱线图，便于详细分析
    for col in numeric_cols:
        plt.figure(figsize=(12, 6))
        
        # 第一子图：直方图+核密度图
        plt.subplot(1, 2, 1)
        sns.histplot(data=df, x=col, kde=True)
        plt.title(f'{col} 分布')
        
        # 第二子图：箱线图
        plt.subplot(1, 2, 2)
        sns.boxplot(data=df, y=col)
        plt.title(f'{col} 箱线图')
        
        plt.tight_layout()
        
        if save_visualizations:
            plt.savefig(os.path.join(vis_dir, f"outlier_analysis_{col}.png"))
            plt.close()
        else:
            plt.show()

    # 2. 批量箱线图，用于整体对比
    batch_size = 4  # 每批显示4个特征，提升可读性
    for i in range(0, len(numeric_cols), batch_size):
        cols = numeric_cols[i:i+batch_size]
        plt.figure(figsize=(16, 8))
        sns.boxplot(data=df[cols], orient="h")
        plt.title(f"数值型特征异常值箱线图 (批次 {i//batch_size+1})")
        plt.tight_layout()
        
        if save_visualizations:
            plt.savefig(os.path.join(vis_dir, f"outlier_boxplot_batch_{i//batch_size+1}.png"))
            plt.close()
        else:
            plt.show()

    # 3. 异常值处理
    df_outlier = df.copy()
    outlier_summary = {}  # 记录每个特征的异常值情况
    
    for col in numeric_cols:
        # 根据选择的方法确定异常值边界
        if method == 'iqr':
            Q1 = df_outlier[col].quantile(0.25)
            Q3 = df_outlier[col].quantile(0.75)
            IQR = Q3 - Q1
            lower = Q1 - whisker * IQR
            upper = Q3 + whisker * IQR
        elif method == 'zscore':
            # Z-score方法，默认阈值为3
            z_scores = np.abs(stats.zscore(df_outlier[col]))
            lower = df_outlier[col].mean() - 3 * df_outlier[col].std()
            upper = df_outlier[col].mean() + 3 * df_outlier[col].std()
        else:
            print(f"不支持的异常值检测方法: {method}，使用默认的IQR方法")
            method = 'iqr'
            Q1 = df_outlier[col].quantile(0.25)
            Q3 = df_outlier[col].quantile(0.75)
            IQR = Q3 - Q1
            lower = Q1 - whisker * IQR
            upper = Q3 + whisker * IQR
        
        # 计算异常值数量
        n_total = len(df_outlier)
        n_lower = (df_outlier[col] < lower).sum()
        n_upper = (df_outlier[col] > upper).sum()
        n_total_outliers = n_lower + n_upper
        outlier_ratio = n_total_outliers / n_total * 100 if n_total > 0 else 0
        
        # 保存异常值信息
        outlier_summary[col] = {
            '总样本数': n_total,
            '下界异常值': n_lower,
            '上界异常值': n_upper,
            '异常值总数': n_total_outliers,
            '异常值比例(%)': round(outlier_ratio, 2),
            '下界': round(lower, 4),
            '上界': round(upper, 4)
        }
        
        # 截断异常值（ Winsorization 方法）
        if n_total_outliers > 0:
            df_outlier[col] = np.where(df_outlier[col] < lower, lower, df_outlier[col])
            df_outlier[col] = np.where(df_outlier[col] > upper, upper, df_outlier[col])
            print(f"{col} - 异常值处理完成: 下界异常值 {n_lower} 个, 上界异常值 {n_upper} 个, 占比 {outlier_ratio:.2f}%")

    # 4. 生成异常值处理摘要报告
    print("\n异常值处理摘要:")
    summary_df = pd.DataFrame(outlier_summary).T
    summary_df = summary_df.sort_values('异常值比例(%)', ascending=False)
    print(summary_df[['异常值总数', '异常值比例(%)', '下界', '上界']])
    
    if save_visualizations:
        # 保存异常值比例可视化
        plt.figure(figsize=(12, 8))
        summary_df['异常值比例(%)'].sort_values().plot(kind='barh')
        plt.title('各特征异常值比例')
        plt.xlabel('异常值比例(%)')
        plt.tight_layout()
        plt.savefig(os.path.join(vis_dir, "outlier_ratio_summary.png"))
        plt.close()
        
        # 保存摘要表格
        summary_df.to_csv(os.path.join(vis_dir, "outlier_summary.csv"))

    # 5. 保存处理后的数据
    try:
        df_outlier.to_csv(output_file, index=False)
        print(f"\n异常值处理完成，结果已保存为 {output_file}")
    except Exception as e:
        print(f"保存处理后的数据时发生错误: {str(e)}")

    return df_outlier, summary_df


# stage 4
# 为了增加检测结果的可靠性，对某些孕妇有多次采血多次检测或一次采血多次检测的情况。
# 检查有多少孕妇存在这些 记录
# 所以，应该如何处理这种情况
def process_nipt_data(input_file, output_file):
    # 读取数据
    df = pd.read_csv(input_file)
    
    # 转换日期列为datetime格式
    df['末次月经'] = pd.to_datetime(df['末次月经'], format='mixed')
    df['检测日期'] = pd.to_datetime(df['检测日期'], format='mixed')
    
    # 计算孕周差（用于权重计算）
    df['days_since_LMP'] = (df['检测日期'] - df['末次月经']).dt.days
    df['gestational_weeks'] = df['days_since_LMP'] / 7
    
    # 定义关键染色体
    key_chromosomes = ['13号染色体的Z值', '18号染色体的Z值', '21号染色体的Z值', 'X染色体的Z值', 'Y染色体的Z值']
    
    # 对每个孕妇分组处理
    grouped = df.groupby('孕妇代码')
    
    results = []
    
    for name, group in grouped:
        # 按检测日期排序
        group = group.sort_values('检测日期')
        
        # 计算权重（基于唯一读段数和孕周）
        group['weight'] = group['唯一比对的读段数'] * group['gestational_weeks']
        
        # 初始化结果字典
        result = {
            '孕妇代码': name,
            '年龄': group['年龄'].iloc[0],
            '身高': group['身高'].iloc[0],
            '体重': group['体重'].mean(),  # 取平均体重
            '末次月经': group['末次月经'].iloc[0],
            'IVF妊娠': group['IVF妊娠'].iloc[0],
            '检测次数': len(group),
            '首次检测日期': group['检测日期'].min(),
            '末次检测日期': group['检测日期'].max(),
            '平均孕周': group['gestational_weeks'].mean(),
            '孕妇BMI': group['孕妇BMI'].mean(),
            '平均原始读段数': group['原始读段数'].mean(),
            '平均唯一读段数': group['唯一比对的读段数'].mean(),
            '怀孕次数': group['怀孕次数'].iloc[0],
            '生产次数': group['生产次数'].iloc[0],
            '胎儿是否健康': group['胎儿是否健康'].iloc[0]  # 取最后一次的结果
        }
        
        # 对每个关键染色体计算加权平均Z值
        for chrom in key_chromosomes:
            weighted_z = np.average(group[chrom], weights=group['weight'])
            result[f'加权平均_{chrom}'] = weighted_z
            result[f'{chrom}_标准差'] = group[chrom].std()
        
        # 计算Y染色体浓度变化
        result['Y染色体浓度_首次'] = group['Y染色体浓度'].iloc[0]
        result['Y染色体浓度_末次'] = group['Y染色体浓度'].iloc[-1]
        result['Y染色体浓度变化'] = group['Y染色体浓度'].iloc[-1] - group['Y染色体浓度'].iloc[0]
        
        # 判断染色体是否异常（|Z|>3）
        abnormal_chromosomes = []
        for chrom in key_chromosomes:
            weighted_z = result[f'加权平均_{chrom}']
            if abs(weighted_z) > 3:
                abnormal_chromosomes.append(chrom.replace('号染色体的Z值', '').replace('染色体的Z值', ''))
        
        result['异常染色体'] = ','.join(abnormal_chromosomes) if abnormal_chromosomes else '正常'
        
        # 添加一致性评估
        consistency = []
        for chrom in key_chromosomes:
            z_values = group[chrom].values
            if all(abs(z) < 2 for z in z_values):
                consistency.append(f"{chrom.split('的')[0]}一致正常")
            elif all(abs(z) > 2 for z in z_values):
                consistency.append(f"{chrom.split('的')[0]}一致异常")
            else:
                consistency.append(f"{chrom.split('的')[0]}不一致")
        
        result['一致性评估'] = '; '.join(consistency)
        
        results.append(result)
    
    # 创建结果DataFrame
    result_df = pd.DataFrame(results)
    
    # 重新排列列顺序
    cols_order = [
        '孕妇代码', '年龄', '身高', '体重', '末次月经', 'IVF妊娠', '检测次数',
        '首次检测日期', '末次检测日期', '平均孕周', '孕妇BMI', '平均原始读段数', '平均唯一读段数',
        '怀孕次数', '生产次数', '胎儿是否健康', '异常染色体', '一致性评估'
    ]
    
    # 添加染色体相关列
    for chrom in key_chromosomes:
        cols_order.extend([
            f'加权平均_{chrom}',
            f'{chrom}_标准差'
        ])
    
    cols_order.extend([
        'Y染色体浓度_首次', 'Y染色体浓度_末次', 'Y染色体浓度变化'
    ])
    
    result_df = result_df[cols_order]
    
    # 保存结果
    result_df.to_csv(output_file, index=False, encoding='utf-8-sig')
    
    return result_df


if __name__=='__main__':
    main()